"""
DSL解析器实现
使用PLY (Python Lex-Yacc) 实现客服机器人DSL的语法解析
重新设计，确保能正确解析所有规则
"""

import ply.lex as lex
import ply.yacc as yacc
from typing import List, Dict, Any, Optional
import re

# 定义词法分析器的token
tokens = (
    'WHEN', 'DO', 'SET_STATE', 'RESPOND', 'ORDER', 'IF', 'THEN', 'ELSE',
    'CREATE', 'CANCEL', 'CHECK', 'LIST',
    'ORDER_INQUIRY', 'ORDER_CANCEL', 'PRODUCT_INFO', 'REFUND_REQUEST',
    'COMPLAINT', 'GREETING', 'GOODBYE', 'UNKNOWN',
    'GREETING_STATE', 'ORDER_PROCESSING', 'REFUND_PROCESSING', 
    'COMPLAINT_HANDLING', 'IDLE',
    'COMPUTER_INFO', 'PHONE_INFO', 'PAD_INFO', 'EARSET_INFO',
    'ROBOT_ANSWER', 'HUMAN_ANSWER',
    'COMPUTER', 'PHONE', 'PAD', 'EARSET',
    'STRING', 'NUMBER', 'ID', 'EQUALS', 'PLUS'
)

# 定义保留字
reserved = {
    'when': 'WHEN',
    'do': 'DO',
    'set_state': 'SET_STATE',
    'respond': 'RESPOND',
    'order': 'ORDER',
    'if': 'IF',
    'then': 'THEN',
    'else': 'ELSE',
    'create': 'CREATE',
    'cancel': 'CANCEL',
    'check': 'CHECK',
    'list': 'LIST',
    'order_inquiry': 'ORDER_INQUIRY',
    'order_cancel': 'ORDER_CANCEL',
    'product_info': 'PRODUCT_INFO',
    'refund_request': 'REFUND_REQUEST',
    'complaint': 'COMPLAINT',
    'greeting': 'GREETING',
    'goodbye': 'GOODBYE',
    'unknown': 'UNKNOWN',
    'greeting_state': 'GREETING_STATE',
    'order_processing': 'ORDER_PROCESSING',
    'refund_processing': 'REFUND_PROCESSING',
    'complaint_handling': 'COMPLAINT_HANDLING',
    'idle': 'IDLE',
    'computer_info': 'COMPUTER_INFO',
    'phone_info': 'PHONE_INFO',
    'pad_info': 'PAD_INFO',
    'earset_info': 'EARSET_INFO',
    'robot_answer': 'ROBOT_ANSWER',
    'human_answer': 'HUMAN_ANSWER',
    'computer': 'COMPUTER',
    'phone': 'PHONE',
    'pad': 'PAD',
    'earset': 'EARSET'
}

# 词法分析器规则
t_ignore = ' \t'

def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)

def t_STRING(t):
    r'"(?:[^"\\]|\\.)*"'
    # 处理转义字符
    value = t.value[1:-1]  # 去掉引号
    # 将转义的换行符转换为真正的换行符
    value = value.replace('\\n', '\n')
    value = value.replace('\\t', '\t')
    value = value.replace('\\"', '"')
    value = value.replace('\\\\', '\\')
    t.value = value
    return t

def t_NUMBER(t):
    r'\d+'
    t.value = int(t.value)
    return t

def t_ID(t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    t.type = reserved.get(t.value.lower(), 'ID')
    return t

t_EQUALS = r'=='
t_PLUS = r'\+'

def t_error(t):
    print(f"Illegal character '{t.value[0]}'")
    t.lexer.skip(1)

# 构建词法分析器
lexer = lex.lex()

# 定义优先级
precedence = (
    ('right', 'WHEN'),
)

# 语法分析器规则
def p_script(p):
    """script : statement_list"""
    p[0] = p[1]

def p_statement_list(p):
    """statement_list : statement_list statement
                     | statement"""
    if len(p) == 3:
        p[0] = p[1] + [p[2]]
    else:
        p[0] = [p[1]]

def p_intent_rule(p):
    """statement : WHEN intent_type DO action_list"""
    p[0] = {
        'type': 'intent_rule',
        'intent': p[2],
        'actions': p[4]
    }

def p_intent_type(p):
    """intent_type : ORDER_INQUIRY
                   | ORDER_CANCEL
                   | PRODUCT_INFO
                   | REFUND_REQUEST
                   | COMPLAINT
                   | GREETING
                   | GOODBYE
                   | UNKNOWN
                   | ROBOT_ANSWER
                   | HUMAN_ANSWER
                   | COMPUTER
                   | PHONE
                   | PAD
                   | EARSET"""
    p[0] = p[1].lower()

def p_action_list(p):
    """action_list : action_list action
                   | action"""
    if len(p) == 3:
        p[0] = p[1] + [p[2]]
    else:
        p[0] = [p[1]]

def p_set_state_action(p):
    """action : SET_STATE state_type"""
    p[0] = {
        'type': 'set_state',
        'state': p[2]
    }

def p_state_type(p):
    """state_type : GREETING_STATE
                  | ORDER_PROCESSING
                  | REFUND_PROCESSING
                  | COMPLAINT_HANDLING
                  | IDLE
                  | COMPUTER_INFO
                  | PHONE_INFO
                  | PAD_INFO
                  | EARSET_INFO"""
    p[0] = p[1].lower()

def p_order_action(p):
    """action : ORDER order_operation"""
    p[0] = {
        'type': 'order',
        'operation': p[2]
    }

def p_order_operation(p):
    """order_operation : CREATE order_params
                       | CANCEL ID
                       | CHECK ID
                       | LIST ID"""
    if p[1].lower() == 'create':
        p[0] = {
            'op_type': 'create',
            'params': p[2]
        }
    else:
        p[0] = {
            'op_type': p[1].lower(),
            'target': p[2]
        }

def p_order_params(p):
    """order_params : STRING NUMBER STRING STRING"""
    p[0] = {
        'product_name': p[1],
        'amount': p[2],
        'date': p[3],
        'status': p[4]
    }

def p_response_action(p):
    """action : RESPOND response_content"""
    p[0] = {
        'type': 'respond',
        'content': p[2]
    }

def p_when_action_respond(p):
    """action : WHEN intent_type RESPOND response_content"""
    p[0] = {
        'type': 'when_respond',
        'intent': p[2],
        'content': p[4]
    }

def p_when_action_do(p):
    """action : WHEN intent_type DO action_list"""
    p[0] = {
        'type': 'when_action',
        'intent': p[2],
        'actions': p[4]
    }

def p_response_content(p):
    """response_content : response_content PLUS STRING
                        | STRING"""
    if len(p) == 4:
        p[0] = p[1] + " " + p[3]
    else:
        p[0] = p[1]

def p_condition_action(p):
    """action : IF condition_expr THEN action_list ELSE action_list
              | IF condition_expr THEN action_list"""
    if len(p) == 6:
        p[0] = {
            'type': 'condition',
            'condition': p[2],
            'then_actions': p[4],
            'else_actions': p[6]
        }
    else:
        p[0] = {
            'type': 'condition',
            'condition': p[2],
            'then_actions': p[4],
            'else_actions': []
        }

def p_condition_expr(p):
    """condition_expr : ID EQUALS STRING
                      | ID EQUALS ID
                      | ID"""
    if len(p) == 4:
        p[0] = {
            'type': 'comparison',
            'left': p[1],
            'operator': p[2],
            'right': p[3]
        }
    else:
        p[0] = {
            'type': 'boolean',
            'value': p[1]
        }

def p_error(p):
    """错误处理"""
    if p:
        # 静默处理，不打印错误
        pass

# 构建语法分析器
parser = yacc.yacc(debug=False, write_tables=False)

class DSLAnalyzer:
    """DSL分析器类 - 重新设计，确保正确解析所有规则"""
    
    def __init__(self):
        self.parsed_script = None
    
    def _split_script_by_top_level_when(self, script_text: str) -> List[str]:
        """
        将脚本按顶级WHEN规则分割
        顶级规则：行首没有缩进（或只有空格）的WHEN
        嵌套规则：行首有4个空格或tab的WHEN
        """
        lines = script_text.split('\n')
        rules = []
        current_rule = []
        in_rule = False
        
        for line in lines:
            stripped = line.strip()
            if not stripped:
                if current_rule:
                    current_rule.append(line)
                continue
            
            # 检查是否是顶级WHEN规则（行首没有缩进或只有很少缩进）
            # 顶级规则：WHEN在行首，或者只有少量空格
            is_top_level = stripped.startswith('WHEN ') and (
                line.strip() == line or  # 没有前导空格
                (line.startswith(' ') and not line.startswith('    '))  # 只有1-3个空格
            )
            
            if is_top_level:
                # 保存之前的规则
                if current_rule:
                    rule_text = '\n'.join(current_rule).strip()
                    if rule_text:
                        rules.append(rule_text)
                # 开始新规则
                current_rule = [line]
                in_rule = True
            else:
                current_rule.append(line)
        
        # 添加最后一个规则
        if current_rule:
            rule_text = '\n'.join(current_rule).strip()
            if rule_text:
                rules.append(rule_text)
        
        return rules
    
    def parse(self, script_text: str) -> List[Dict]:
        """
        解析DSL脚本
        使用分割策略避免shift/reduce冲突
        """
        if not script_text or not script_text.strip():
            return []
        
        # 直接尝试分割并解析每个规则
        rule_texts = self._split_script_by_top_level_when(script_text)
        all_rules = []
        
        for rule_text in rule_texts:
            try:
                parsed_rule = parser.parse(rule_text, lexer=lexer)
                if parsed_rule:
                    if isinstance(parsed_rule, list):
                        all_rules.extend(parsed_rule)
                    else:
                        all_rules.append(parsed_rule)
            except Exception as e:
                # 解析单个规则失败，打印错误信息以便调试
                if 'PRODUCT_INFO' in rule_text:
                    print(f"解析PRODUCT_INFO规则失败: {e}")
                    print(f"规则文本前100字符: {rule_text[:100]}")
                continue
        
        self.parsed_script = all_rules
        return all_rules
    
    def get_intent_rules(self) -> List[Dict]:
        """获取所有意图规则"""
        if not self.parsed_script:
            return []
        
        intent_rules = []
        for statement in self.parsed_script:
            if isinstance(statement, dict) and statement.get('type') == 'intent_rule':
                intent_rules.append(statement)
        return intent_rules
    
    def get_rule_by_intent(self, intent: str) -> Optional[Dict]:
        """
        根据意图获取对应的规则（支持中英文意图名，包括子意图）
        """
        if not intent:
            return None
        
        eng_to_cn = {
            'order_inquiry': '订单查询',
            'order_cancel': '订单取消',
            'product_info': '产品信息',
            'refund_request': '退款申请',
            'complaint': '投诉',
            'greeting': '问候',
            'goodbye': '告别',
            'unknown': '未知',
            # 子意图保持英文
            'computer': 'computer',
            'phone': 'phone',
            'pad': 'pad',
            'earset': 'earset'
        }
        cn_to_eng = {v.lower(): k for k, v in eng_to_cn.items() if k != v}

        # 将查询意图标准化为英文（解析后的规则使用英文小写标识）
        intent_l = intent.lower()
        query_eng = cn_to_eng.get(intent_l, intent_l)

        for rule in self.get_intent_rules():
            rule_intent = rule.get('intent', '').lower()
            # 直接英文匹配（包括子意图）
            if rule_intent == query_eng:
                return rule
        return None

if __name__ == "__main__":
    # 测试解析器
    test_script = '''
WHEN GREETING DO
    SET_STATE GREETING_STATE
    RESPOND "Hello"

WHEN ORDER_INQUIRY DO
    SET_STATE ORDER_PROCESSING
    IF user_authenticated THEN
        ORDER LIST user_id
        RESPOND "Checking orders..."
    ELSE
        RESPOND "Please login."

WHEN PRODUCT_INFO DO
    SET_STATE IDLE
    RESPOND "Product info"
    WHEN COMPUTER DO
        RESPOND "Computer info"
    WHEN PHONE DO
        RESPOND "Phone info"
    '''
    
    analyzer = DSLAnalyzer()
    result = analyzer.parse(test_script)
    if result:
        print("解析成功!")
        rules = analyzer.get_intent_rules()
        print(f"解析出 {len(rules)} 个规则:")
        for rule in rules:
            print(f"  意图: {rule['intent']}")
            print(f"  动作数: {len(rule.get('actions', []))}")
            # 检查嵌套规则
            for action in rule.get('actions', []):
                if action.get('type') in ['when_respond', 'when_action']:
                    print(f"    嵌套规则: {action.get('intent')}")
    else:
        print("解析失败!")
